package database

import (
	"context"
	"goGameWebServer/internal/common/config"
	"goGameWebServer/internal/common/database/entityHelper"
	"goGameWebServer/internal/common/database/mysql"
	"goGameWebServer/internal/common/logger"
	"goGameWebServer/internal/common/utils"
	"goGameWebServer/internal/interfaces"
	"strings"

	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/container/gmap"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/spf13/viper"
)

const ENTITY_PATH = "/res/entities"

type MysqlDatabase struct {
	BaseDataBase
}

func (mDataBase *MysqlDatabase) Init() {
	mDataBase.BaseDataBase.Init()
	if mDataBase.Options.LoadEntity {
		err := mDataBase.SyncTable()
		if err != nil {
			logger.LoggerIns().Errorf("mysql database init error: %s", err.Error())
		}
	}
}

func (mDataBase *MysqlDatabase) CheckTableIsExist(tableName string) (bool, error) {
	sqlStrBuilder := strings.Builder{}
	sqlStrBuilder.WriteString("SHOW TABLES LIKE '" + tableName + "';")
	re, err := mDataBase.GetDb().Query(context.TODO(), sqlStrBuilder.String())
	if err != nil {
		return false, err
	}
	if re.Len() > 0 {
		return true, nil
	}
	return false, nil
}

func (mDataBase *MysqlDatabase) QueryDifferenceField(tableName string, configFields []entityHelper.DBEntityField) *gmap.StrAnyMap {
	sqlStrBuilder := strings.Builder{}
	fieldMap := gmap.StrAnyMap{}
	for _, configField := range configFields {
		fieldMap.Set(configField.FieldName, configField)
	}
	sqlStrBuilder.WriteString("SHOW COLUMNS FROM " + tableName)
	reColumns, err := mDataBase.GetDb().Query(context.TODO(), sqlStrBuilder.String())
	if err != nil {
		panic(err)
	}
	allColumns := reColumns.List()
	for _, column := range allColumns {
		if fieldMap.Contains(column["Field"].(string)) {
			fieldMap.Remove(column["Field"].(string))
		}
	}
	return &fieldMap
}

func (mDataBase *MysqlDatabase) SyncTable() error {
	if mDataBase.Options.LoadEntity {
		appRunPath := gfile.SelfDir()
		entityPath := appRunPath + ENTITY_PATH
		err := mDataBase.SyncTableFormConfig(entityPath)
		if err != nil {
			return err
		}
	}
	return nil
}

func (mDataBase *MysqlDatabase) SyncTableFormConfig(entityPath string) error {
	entityConfigFlies, err := utils.FindFilesByType(entityPath, "yaml", false)
	if err != nil {
		return err
	}
	// 遍历配置创建表
	yamlConfig := viper.GetViper()
	yamlConfig.AddConfigPath(entityPath)
	yamlConfig.SetConfigType("yaml")
	for _, entityConfigFile := range entityConfigFlies {
		yamlConfig.SetConfigName(entityConfigFile)
		if err := yamlConfig.ReadInConfig(); err != nil {
			panic(err)
		}
		dbEntity := entityHelper.NewDBEntity()
		if err := yamlConfig.Unmarshal(dbEntity); err != nil {
			panic(err)
		}
		if mDataBase.CheckEntityIsExist(dbEntity.EntityName) {
			logger.LoggerIns().Errorf("entity %s is exist", dbEntity.EntityName)
			continue
		}
		mDataBase.UpdateEntityData(dbEntity.EntityName, dbEntity)
		if err := mDataBase.SyncTableByEntity(dbEntity); err != nil {
			return err
		}
	}
	return nil
}

func (mDataBase *MysqlDatabase) SyncModuleTableFormConfig(entityName string, configPath string) error {
	entityConfigFlies, err := utils.FindFilesByType(configPath, "yaml", false)
	if err != nil {
		return err
	}
	if !mDataBase.CheckEntityIsExist(entityName) {
		logger.LoggerIns().Errorf("entity %s is not exist,module init failed", entityName)
		return nil
	}
	// 遍历配置创建表
	yamlConfig := viper.GetViper()
	yamlConfig.AddConfigPath(configPath)
	yamlConfig.SetConfigType("yaml")
	for _, entityConfigFile := range entityConfigFlies {
		yamlConfig.SetConfigName(entityConfigFile)
		if err := yamlConfig.ReadInConfig(); err != nil {
			panic(err)
		}
		dbEntity := entityHelper.NewDBEntity()
		if err := yamlConfig.Unmarshal(dbEntity); err != nil {
			panic(err)
		}
		if mDataBase.CheckModuleExist(entityName, dbEntity.EntityName) {
			logger.LoggerIns().Errorf("entity %s module %s is exist", entityName, dbEntity.EntityName)
			continue
		}
		mDataBase.AppendModuleName(entityName, dbEntity.EntityName)
		dbEntity.TableName = "module_" + dbEntity.TableName
		// 将共享字段添加到字段列表中
		for _, moduleShareField := range mDataBase.GetModuleShareFields() {
			dbEntity.Fields = append(dbEntity.Fields, *moduleShareField.(*entityHelper.DBEntityField))
		}
		if err := mDataBase.SyncTableByEntity(dbEntity); err != nil {
			return err
		}
	}
	return nil
}

func (mDataBase *MysqlDatabase) SyncTableByEntity(entity *entityHelper.DBEntity) error {
	sqlStrBuilder := &strings.Builder{}
	tableName := entity.EntityName
	if entity.TableName != "" {
		tableName = entity.TableName
	}
	if mDataBase.GetPrefix() != "" {
		tableName = mDataBase.GetPrefix() + tableName
	}
	if exist, err := mDataBase.CheckTableIsExist(tableName); err != nil {
		return err
	} else if exist {
		differenceFields := mDataBase.QueryDifferenceField(tableName, entity.Fields)
		if !differenceFields.IsEmpty() {
			mysql.StartAlterTable(tableName, sqlStrBuilder)
			allCount := differenceFields.Size()
			fillCount := 0
			differenceFields.Iterator(func(key string, value interface{}) bool {
				dbField := value.(entityHelper.DBEntityField)
				mysql.AlterTableField(dbField, sqlStrBuilder)
				fillCount += 1
				if fillCount == allCount {
					mysql.EndAlterTable(sqlStrBuilder)
				} else {
					sqlStrBuilder.WriteString(",")
				}
				return true
			})
			_, sqlErr := mDataBase.GetDb().Exec(context.TODO(), sqlStrBuilder.String())
			if sqlErr != nil {
				return sqlErr
			}
		}
		return nil
	}
	mysql.StartCreateTable(tableName, sqlStrBuilder)
	sqlStrBuilder.WriteString(mysql.SQLFEILD_START)
	indexKeys := garray.New()
	for i, dbEntityField := range entity.Fields {
		mysql.FillField(&dbEntityField, sqlStrBuilder)
		if dbEntityField.FieldIndex == 1 {
			indexKeys.Append(dbEntityField)
		}
		if i != len(entity.Fields)-1 {
			sqlStrBuilder.WriteString(",")
		}
	}
	mysql.FillIndexKeys(indexKeys, sqlStrBuilder)
	sqlStrBuilder.WriteString(mysql.SQLFEILD_END)
	sqlStrBuilder.WriteString(mysql.SQLTABLE)
	mysql.EndCreateTable(sqlStrBuilder)

	_, err := mDataBase.GetDb().Exec(context.TODO(), sqlStrBuilder.String())
	if err != nil {
		return err
	}

	return nil
}

func (mDataBase *MysqlDatabase) AnalysisModuleShareField(moduleSharePath string) error {
	moduleShareConfigFlies, err := utils.FindFilesByType(moduleSharePath, "yaml", false)
	if err != nil {
		return err
	}
	// 遍历配置创建表
	yamlConfig := viper.GetViper()
	yamlConfig.AddConfigPath(moduleSharePath)
	yamlConfig.SetConfigType("yaml")
	for _, configFile := range moduleShareConfigFlies {
		yamlConfig.SetConfigName(configFile)
		if err := yamlConfig.ReadInConfig(); err != nil {
			panic(err)
		}
		dbFields := entityHelper.NewDBEntityModuleFields()
		if err := yamlConfig.Unmarshal(dbFields); err != nil {
			panic(err)
		}
		// 遍历Fields增加到字典中
		for _, dbField := range dbFields.Fields {
			if mDataBase.CheckModuleShareField(dbField.FieldName) {
				logger.LoggerIns().Errorf("module share field %s is exist", dbField.FieldName)
				continue
			}
			mDataBase.AppendModuleShareField(&dbField)
		}
	}
	return nil
}

func NewMysqlDatabase(ctx context.Context, option config.DatabaseOption) (interfaces.IDataBase, error) {
	return &MysqlDatabase{
		BaseDataBase: NewBaseDateBase(ctx, option),
	}, nil
}
